﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WebeTerm.PlugIns;
using System.Text.RegularExpressions;
using eTerm.ASynClientSDK;

namespace WebeTerm.RobotBook
{
    /// <summary>
    /// 自动订票插件
    /// </summary>
    public sealed class RobotBookPlugIn : BaseTermSequencePlugIn<RobotBookResult,RobotBookConfig>
    {
        #region 私有变量定义
        //保存指令完整类容
        private StringBuilder sb = new StringBuilder();
        private Queue<RequireBook> m_RequireBook = new Queue<RequireBook>();
        private RequireBook m_CurrentPNRequest = null;
        private string m_CurrentCmd = @"AV";
        private string queryDate = @"22OCT";
        private string SSTemplate = @"{0}
{1}
{2}
tktl/{3}/./SJW161
ct:13500000000 zhangsan                                                      
RMK MP 13500000000
@KI";
        #endregion

        #region 构造函数
        public RobotBookPlugIn() {
            this.Works.Add(this.SI);
            this.Works.Add(this.IG);
            this.Works.Add(this.EXIT);
            this.Works.Add(this.AV);
            this.Works.Add(this.PN);
            this.Works.Add(this.SS);
            this.Works.Add(this.RT);
        }
        #endregion

        #region 指令序列处理
        /// <summary>
        /// 发送一条无效指令，退出配置
        /// </summary>
        /// <param name="Cmd"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        private bool EXIT(ref string Cmd, ref  string Result) {
            if (Regex.IsMatch(Cmd, @"^EXIT", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {
                Disconnect();
                return true;
            }
            return false;
        }


        /// <summary>
        /// Sses the specified command.
        /// </summary>
        /// <param name="Cmd">The command.</param>
        /// <param name="Result">The result.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        private bool SS(ref string Cmd, ref  string Result) {
            if (Regex.IsMatch(Cmd, @"^SS", RegexOptions.IgnoreCase | RegexOptions.Multiline)) {
                GetPNRCode(Result);
                //SendStream(@"IG");
                return true;
            }
            return false;

            //GetPNRCode(Result);
        }

        /// <summary>
        /// 起始指令
        /// </summary>
        /// <param name="Cmd"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        private bool SI(ref string Cmd, ref  string Result)
        {
            if (Regex.IsMatch(Cmd, @"^SI|^BEGIN", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {
                InitQueue();
                return true;
            }
            return false;
        }

        private bool RT(ref string Cmd, ref string Result) {
            if (Regex.IsMatch(Cmd, @"^RT", RegexOptions.IgnoreCase | RegexOptions.Multiline) || (Regex.IsMatch(m_CurrentCmd, @"^RT", RegexOptions.IgnoreCase | RegexOptions.Multiline) && Regex.IsMatch(m_CurrentCmd, @"^PN", RegexOptions.IgnoreCase | RegexOptions.Multiline)))
            {
                if (string.IsNullOrEmpty(GetCode(Result))) {
                    SendStream(@"PN");
                    return false;
                }
                else
                {
                    SendStream(@"IG");
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 下一条待处理
        /// </summary>
        /// <param name="Cmd"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        private bool IG(ref string Cmd, ref  string Result)
        {
            if (Regex.IsMatch(Cmd, @"^IG", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {
                Dequeue();
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取查询结果处理
        /// </summary>
        /// <param name="Cmd"></param>
        /// <param name="Result"></param>
        /// <returns></returns>
        private bool AV(ref string Cmd, ref  string Result)
        {
            if (Regex.IsMatch(Cmd, @"^AV", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {
                return true;
            }
            return false;
        }


        private bool PN(ref string Cmd, ref  string Result) {
            if (Regex.IsMatch(Cmd, @"^PN", RegexOptions.IgnoreCase | RegexOptions.Multiline))
            {
                if (m_CurrentCmd == @"AV")
                {
                    return true;
                }
                else if (m_CurrentCmd == @"RT"&&!string.IsNullOrEmpty( GetCode(Result))) {
                    m_CurrentPNRequest.Code = GetCode(Result);
                    SendStream(@"IG");
                    return true;
                }
                else if (m_CurrentCmd == @"RT" && !string.IsNullOrEmpty(GetCode(Result))) {
                    SendStream(@"PN");
                    return false;
                }
            }
            return false;
        }
        #endregion


        #region 获取可用航班、可以舱位
        /// <summary>
        /// 指令结果解析适配器.
        /// </summary>
        /// <param name="Msg">指令结果集合.</param>
        /// <returns></returns>
        private AVResult ResultAdapter(string Msg)
        {
            AVResult result = new AVResult() { getDate = DateTime.Now.AddDays(1), getDst = @"SHA", getOrg = @"CSX", AvSegment = new List<AvItem>() };
            DateTime dtDate = result.getDate.Value;
            //queryDate = string.Format(@"{0}{1}", dtDate.Day.ToString("D2"), getDateString(dtDate));
            sb = new StringBuilder();
            string avCommand = string.Format(@"AV:H/{0}{1}/{2}{3}{4}"
                , result.getOrg
                , result.getDst
                , string.Format(@"{0}{1}", dtDate.Day.ToString("D2"), getDateString(dtDate))
                , string.IsNullOrEmpty(@"MU") ? string.Empty : "/" + @"MU"
                , "/D"
                );
            foreach (Flight seg in new AnalysisAVH().ParseAVH(avCommand, Msg).Flights)
            {
                //if (result.AvSegment.Where(s => s.getAirline == seg.FlightNO).Count() > 0) continue;
                AvItem AvSegment = new AvItem()
                {
                    getAirline = seg.FlightNO,
                    getArritime = seg.ArrivalTime.Insert(2, ":"),
                    getCarrier = seg.Airline,
                    getDeptime = seg.DepartureTime.Insert(2, ":"),
                    getDstcity = seg.ArrivalAirport,
                    getMeal = seg.Meal.Trim().Length > 0,
                    getPlanestyle = seg.AircraftType,
                    getOrgcity = seg.DepartureAirport,
                    getStopnumber = int.Parse(seg.Stop),
                    isCodeShare = seg.CodeShare,
                    getLink = seg.Connect,
                    getDepdate = queryDate,
                    getArridate = queryDate,
                    getCabins = new List<AvItemCabinChar>(),
                    getArritimemodify = seg.ArrivalDate,
                    getArrivalTerminal = seg.ArrivalTerminal,
                    getDepartTerminal = seg.DepartTerminal,
                    getRunTimeDuration = seg.RunTimeDuration

                };
                foreach (FlightCarbin carbin in seg.Carbins.Where<FlightCarbin>(CARBIN => Regex.IsMatch(CARBIN.Number, @"[1-9A]", RegexOptions.IgnoreCase | RegexOptions.Multiline)))
                {
                    AvSegment.getCabins.Add(new AvItemCabinChar()
                    {
                        getCode = carbin.Carbin,
                        getAvalibly = carbin.Number,
                    });
                }
                result.AvSegment.Add(AvSegment);
            }
            return result;
        }

        /// <summary>
        /// 获取有效航班舱位
        /// </summary>
        /// <param name="avResult"></param>
        /// <param name="avResultMessage"></param>
        private void GetAvailable(AVResult avResult, string avResultMessage)
        {
            try
            {
                var available = avResult.AvSegment
                    .Where(l =>
                            (l.getOrgcity == m_CurrentPNRequest.Dept && l.getDstcity == m_CurrentPNRequest.Dest)
                            &&
                            (l.getCarrier == m_CurrentPNRequest.AirWay)
                        )
                    .Where(l => !l.isCodeShare && l.getCabins.Count > 0)//.Join(cabinCache,l=>l.getCarrier,r=>r.AirWay,(l,r)=>l)

                    .Select(l => new
                    {
                        Segment = l,
                        MinCabin = l.getCabins.Where(q => q.getAvalibly == "A" || (int.Parse(q.getAvalibly) >=1)).SelectMany(c => this.PlugInConfig.CabinChars.Where(r => r.AirWay == l.getCarrier && r.CabinCharCode == c.getCode ), (h, g) => new
                        {
                            h.getCode,
                            h.getAvalibly,
                            g.Weight
                        })
                        .OrderBy(v => v.Weight)
                        .FirstOrDefault()
                    }).Distinct()
                    .OrderBy(l => l.MinCabin.Weight)
                    .FirstOrDefault();

                if (available != null)
                {
                    string avResultItem = string.Format(@"{0}{1} {2} {3} {4}{5} {6}", available.Segment.getCarrier, available.Segment.getAirline, available.MinCabin.getCode, available.Segment.getDepdate, available.Segment.getOrgcity, available.Segment.getDstcity, 1);
                    m_CurrentPNRequest.AvResultItem .Add( avResultItem);
                    if (!string.IsNullOrEmpty(m_CurrentPNRequest.RetDate) && !m_CurrentPNRequest.IsCompleted)
                    {  //是否有返程日期
                        var tmp = m_CurrentPNRequest.Dept;
                        m_CurrentPNRequest.Dept = m_CurrentPNRequest.Dest;
                        m_CurrentPNRequest.Dest = tmp;
                        SendAV(m_CurrentPNRequest.Dept, m_CurrentPNRequest.Dest, m_CurrentPNRequest.AirWay, m_CurrentPNRequest.RetDate);
                        m_CurrentPNRequest.IsCompleted = true;
                    }
                    else
                    {
                        if (m_CurrentPNRequest.AvResultItem.Count > 0) {
                            SendSS();
                        } else {
                            SendStream(@"IG");
                        }
                    }
                }
                else
                {
                    //无有效航班舱位，是否有一页数据
                    if (ExistNextPage(avResultMessage))
                    {
                        SendStream(@"PN");
                    }
                    else
                    {// 无有效航班舱位，并已无下页数据,开始处理下一条请求
                        SendStream(@"IG");
                    }
                }
            }
            catch
            {
                SendStream(@"IG");
            }
        }
        #endregion

        #region 辅助函数
        /// <summary>
        /// 获取日期字符串辅助方法.
        /// </summary>
        /// <param name="date">The date.</param>
        /// <returns></returns>
        private string getDateString(DateTime date)
        {
            MatchCollection regx = new Regex(@"[A-Z]{3,3}").Matches(date.ToString("R").ToUpper());
            return string.Format(@"{0}", regx[1].Value, date.ToString("yy"));
        }

        /// <summary>
        /// 是否还有下页数据（将自动执行“PnCommand”）.
        /// </summary>
        /// <param name="msgBody">当前指令结果.</param>12JAN11
        /// <returns></returns>
        /// <value><c>true</c> if [exist next page]; otherwise, <c>false</c>.</value>
        private bool ExistNextPage(string msgBody)
        {
            //if (queryDate.Length > 5 && queryDate.Substring(5, 2) == DateTime.Now.ToString(@"yy"))
            //    return Regex.IsMatch(msgBody, string.Format(@"\s+{0}\([A-Z]{{3}}\)\s+", this.queryDate.Substring(0, 5)));
            //else
            return Regex.IsMatch(msgBody, this.queryDate);
        }
        #endregion

        #region 获取PNR大记录编号
        /// <summary>
        /// 获取PNR大记录编号
        /// </summary>
        /// <param name="Result"></param>
        /// <returns></returns>
        private string GetCode(string Result) {
            string code = string.Empty;
            Match m = Regex.Match(Result, @"\s{0,}\d+\.RMK\s+CA\/([A-Z0-9]+)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            if (m.Success) {
                code = m.Groups[1].Value;
            }
            return code;
        }
        #endregion

        #region 订票队队列管理
        /// <summary>
        /// 读取配置数据中的订票队列
        /// </summary>
        private void InitQueue() {
            ReadJSonConfig();
            this.PlugInConfig.Requires.ForEach(l => {
                m_RequireBook.Enqueue(l);
            });
            //自动发启队列
            Dequeue();
        }

        /// <summary>
        /// 获取下一个订票对象,并发送AV指令
        /// </summary>
        private void Dequeue()
        {
            if (m_RequireBook.Count > 0)
            {
                m_CurrentPNRequest = m_RequireBook.Dequeue();
                //发送AV
                SendAV(m_CurrentPNRequest.Dept, m_CurrentPNRequest.Dest, m_CurrentPNRequest.AirWay, m_CurrentPNRequest.DeptDate);
            }
            else {
                SendStream(@"EXIT");
            }
        }
        #endregion
         

        #region AV
        private void SendAV(string dept, string dest, string airway, string deptDate)
        {
            m_CurrentCmd = @"AV";
            bool IsRegex = Regex.IsMatch(deptDate, @"\d{2}[A-Z]{3}", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            queryDate = IsRegex ? deptDate : string.Format(@"{0}{1}", DateTime.Parse(deptDate).Day.ToString("D2"), IsRegex ? deptDate : getDateString(DateTime.Parse(deptDate)));
            sb = new StringBuilder();
            string avCommand = string.Format(@"AV:H/{0}{1}/{2}{3}{4}"
                , dept
                , dest
                , queryDate
                , string.IsNullOrEmpty(airway) ? string.Empty : "/" + airway
                , "/D"
                );
            //FirstCmd = FirstCmd.AV;
            SendStream(avCommand);
        }
        #endregion

        #region SEND SS
        private void SendSS(){
            var segStr =string.Join("\r\n", m_CurrentPNRequest.AvResultItem.ToArray()
                .Select(l => string.Format("SS {0}", l)).ToArray());

            var psgStr = @"NM" + string.Join("", m_CurrentPNRequest.Passengers.Select(l => string.Format(@"1{0}",l.Name)).ToArray());
            StringBuilder idNO = new StringBuilder(@"");
            int index = 1;
            m_CurrentPNRequest.Passengers.ForEach(p => {
                idNO.AppendFormat(@"SSR FOID YY HK/NI{0}/P{1}", p.IdNo, index++);
            });
            var tktl = DateTime.Now.AddHours(5).ToString(@"HH") + "00"; //string.Format(@"tktl/1800/./sjw161", DateTime.Now.ToString(@""));
            var ssCmd = string.Format(this.PlugInConfig.SSCommand??SSTemplate, segStr, psgStr, idNO.ToString(), tktl);
            ssCmd = string.Join("\r\n", ssCmd.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).ToArray());
            m_CurrentCmd = @"SS";
            SendStream( ssCmd);
        }
        #endregion


        #region 获取预订结果
        private void GetPNRCode(string Result)
        {
            var pgdx = m_CurrentPNRequest;
            try
            {
                string PNRCode = ParsePnr(Result);
                if (!string.IsNullOrEmpty(PNRCode))
                {
                    if (string.IsNullOrEmpty(pgdx.PNRNO))
                        pgdx.PNRNO = PNRCode;
                    else
                        pgdx.PNRNO = string.Format(@"{0}({1})", pgdx.PNRNO, PNRCode);
                    //pgdx.Save();
                    //var groupCurrent = Require.All().FirstOrDefault(l => l.ID == RequireId);
                    m_CurrentCmd = @"RT";
                    SendStream(string.Format(@"RT {0}", pgdx.PNRNO));
                }
                else
                {
                    SendStream(@"IG");
                    //DisplayInfo(LogEnumType.ERROR, string.Format(@"订票需求 {0}({1}) 订座失败。", RequireId, pgdx.PNRCode));
                }
            }
            catch (Exception ex)
            {
                //DisplayInfo(LogEnumType.ERROR, string.Format(@"订票需求 {0}({1}) 订座失败:{2}。", RequireId, pgdx.PNRCode, ex.Message));
            }
        }

        /// <summary>
        /// Parses the PNR.
        /// </summary>
        /// <param name="Ss">The ss.</param>
        /// <param name="Msg">The MSG.</param>
        private string ParsePnr(string Msg)
        {
            string Result = string.Empty;
            if (Regex.IsMatch(Msg, @"\s*(\w{6})\s+(\-)+\s*", RegexOptions.IgnoreCase | RegexOptions.Multiline))
                Result = Regex.Match(Msg, @"\s*(\w{6})\s+(\-)+\s*", RegexOptions.Multiline | RegexOptions.IgnoreCase).Groups[1].Value;
            return Result;
        }

        #endregion

        /// <summary>
        /// Gets the stream result.
        /// </summary>
        /// <param name="Cmd">The command.</param>
        /// <param name="Result">The result.</param>
        /// <returns>List{AutoBookingResult}.</returns>
        /// <exception cref="System.NotImplementedException"></exception>
        protected override List<RobotBookResult> GetStreamResult(string Cmd, string Result)
        {
            var retItems  = new List<RobotBookResult>();
            if (m_CurrentCmd == @"AV" && 
                    (
                        Regex.IsMatch(Cmd, @"^AV", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                        ||
                        Regex.IsMatch(Cmd, @"^PN", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    )
                ) {
                    GetAvailable(ResultAdapter(Result), Result);
                    var singleResult = new RobotBookResult();
                    singleResult.RobotRequire = m_CurrentPNRequest;
                    retItems.Add(singleResult);
                }
            else if (m_CurrentCmd == @"SS" &&
                    (
                        Regex.IsMatch(Cmd, @"^SS", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                        ||
                        Regex.IsMatch(Cmd, @"^PN", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    )
                )
            {
                var singleResult = new RobotBookResult();
                singleResult.RobotRequire = m_CurrentPNRequest;
                retItems.Add(singleResult);
            }
            else if (m_CurrentCmd == @"RT" &&
                    (
                        Regex.IsMatch(Cmd, @"^PN", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                        ||
                        Regex.IsMatch(Cmd, @"^RT", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                    )
                ) {
                    var singleResult = new RobotBookResult();
                    m_CurrentPNRequest.Code = GetCode(Result);
                    singleResult.RobotRequire = m_CurrentPNRequest;
                    retItems.Add(singleResult);
                }
            return retItems;
        }
    }
}
